#include <iostream>
#include <fstream>
#include <vector>
#include <stdio.h>
#include <string>
#include <sstream>
#include <set>
#include <athread.h>
#include <sys/time.h>

using namespace std;

typedef struct timeval TIME_T;
#define MARK_TIME(t) gettimeofday(&t, NULL)
#define DIFF_TIME(start, end) ((end.tv_sec-start.tv_sec)*1e3+(end.tv_usec-start.tv_usec)*1e-3) // ms

using weight = float;
float d = 0.85f;

struct edge {
	size_t src;
	size_t dst;
	weight w;
};

void load_edge_from_csv(string fname, vector<size_t> &vertex_list, vector<edge>& edge_list) {
	vertex_list.clear();
	edge_list.clear();

	ifstream in_file(fname, ios::in);
	if(!in_file){
		printf("open csv fail!\n");
		cout << fname << endl;
	}
	string line;
	string value_str;

	getline(in_file, line);
	set<uint64_t> vertex_set;
	edge e;
	while (getline(in_file, line)) {
		istringstream readstr(line); //string��������
		// read idx
		// getline(readstr, value_str, ','); 

		//read src
		getline(readstr, value_str, ','); 
		e.src = atoi(value_str.c_str()); 
		// read dst
		getline(readstr, value_str, ','); 
		e.dst = atoi(value_str.c_str()); 
		// read w
		getline(readstr, value_str, ','); 
		e.w = atof(value_str.c_str());
		
		edge_list.push_back(e);
		vertex_set.insert(e.src);
		vertex_set.insert(e.dst);
	}

	in_file.close();

	size_t nvertex = vertex_set.size();
	
	vertex_list.resize(nvertex);
	for (size_t i = 0; i < nvertex; i++) {
		vertex_list[i] = i;
	}

	return ;
}

void store_wpr_score_to_csv(string fname, const vector<float> score, size_t nvertex) {
	ofstream out_file(fname.c_str(), ios::out);
	
	out_file << "VERTEX,WPR_SCORE" << endl;
	for (size_t i = 0; i < nvertex; i++) {
		out_file << i << "," << score[i] << endl;
	}
	out_file.close();
	return;
}

int main(int argc, char** argv) {

	athread_init();

	TIME_T st, ed,st_pre,ed_pre;

	MARK_TIME(st_pre);

	vector<size_t> vertex_list;
	vector<edge> edge_list;

	string my_input_data_folder = "../data/csv_data/";
	string my_output_data_folder = "../data/openrank_csv_data/";

	string fname = argv[1];
	string fpath = my_input_data_folder + fname+".csv";
	cout << fpath << endl;

	load_edge_from_csv(fname, vertex_list, edge_list);

	auto nvertex = vertex_list.size() + 1;

	vector<float> in_degree(nvertex);
	vector<float> out_degree(nvertex);

	vector<float> w_in_denominator(nvertex);
	vector<float> w_out_denominator(nvertex);
	vector<float> w_in_numerator(nvertex);
	vector<float> w_out_numerator(nvertex);

	vector<float> interm(nvertex);
	vector<float> score(nvertex);

	for (size_t i = 0; i < nvertex; i++) {
		score[i] = 1.0 / (nvertex - 1);
	}

	for (const auto e : edge_list) {
		in_degree[e.dst] += e.w;
		out_degree[e.src] += e.w;

		w_in_numerator[e.dst] += e.w;
		w_out_numerator[e.src] += e.w;
	}

	for (const auto e : edge_list) {
		w_in_denominator[e.src] += in_degree[e.dst];
		w_out_denominator[e.src] += out_degree[e.dst];
	}

	cout <<"Graph Info: "<<edge_list.size() <<" edges, "<<vertex_list.size()<<" vertexs" <<endl;

	MARK_TIME(ed_pre);
	MARK_TIME(st);

	for (size_t i = 0; i < 8; i++) {
		for(size_t i = 0; i < nvertex; i++){
			interm[i] = 0.;
		}

		for (const auto e : edge_list) {
			auto v = e.src;
			auto u = e.dst;
			auto w = e.w;
			interm[u] += score[v] * w_in_numerator[u] * w_out_numerator[u] / (w_in_denominator[v] * w_out_denominator[v]);
		}

		for (const auto v : vertex_list) {
			auto next = (1 - d) + d * interm[v];
			score[v] = next;
		}
	}

	MARK_TIME(ed);

	printf("Loading and Init Time Cost:%lf ms\n",DIFF_TIME(st_pre,ed_pre));
	printf("Weighted PageRank Iteration cost:%lf ms\n",DIFF_TIME(st,ed));

	store_wpr_score_to_csv(my_output_data_folder+fname+".csv", score, nvertex);

	athread_halt();
	return 0;
}